#ifndef __INC_QGRDPCT_H__
#define __INC_QGRDPCT_H__

#ifdef _MSC_VER
#pragma once
#endif

//////////////////////////////////////////////////////////////////////////
// depict
class KAPI qgDpct : public qgObj
{
	KCH_SUBCLASS(qgDpct, qgObj);

public:
	struct Link
	{
		qgDpct*			parent;
		qgDpct*			next;
		qgDpct*			prev;

		ksize_t			sub_count;
		qgDpct*			sub_frst;
		qgDpct*			sub_last;
	};

protected:
	tpl::BlueStr<64>	_name;
	ksize_t				_nhash;

	kint				_id;

	bool				_visible;
	bool				_update;
	bool				_draw;
	bool				_test;

	tpl::Mat4			_calc;
	tpl::Mat4			_local;
	tpl::TrFm			_trfm;

	tpl::Vec3			_bmin;
	tpl::Vec3			_bmax;
	tpl::Vec3			_bctr;
	float				_brad;

	// cull type
	// distance

	Link				_link;

protected:
	qgDpct();
	~qgDpct();

public:
	virtual bool Update(float adv);
	virtual void Draw();

	virtual void SetLocation(const tpl::Vec3& v);
	virtual void SetRotation(const tpl::Quat& q);
	virtual void SetScaling(const tpl::Vec3& v);
	
	const tpl::BlueStr<64>& GetName() { return _name; }
	ksize_t GetNHash() { return _nhash; }
	kint GetId() { return _id; }
	bool IsVisible() { return _visible; }
	bool IsUpdate() { return _update; }
	bool IsDraw() { return _draw; }
	bool IsTest() { return _test; }
	const tpl::Mat4& GetCalcTM() { return _calc; }
	const tpl::Mat4& GetLocalTM() { return _local; }
	const tpl::TrFm& GetTrfm() { return _trfm; }
	const tpl::Vec3& GetLocation() { return _trfm.loc; }
	const tpl::Quat& GetRotation() { return _trfm.rot; }
	const tpl::Vec3& GetScaling() { return _trfm.scl; }
	const tpl::Vec3& GetBoundMin() { return _bmin; }
	const tpl::Vec3& GetBoundMax() { return _bmax; }
	const tpl::Vec3& GetBoundCenter() { return _bctr; }
	float GetBoundRadius() { return _brad; }

	void SetName(const char* name);
	void SetId(kint id) { _id = id; }
	void SetVisible(bool v) { _visible = v; }
	void SetUpdate(bool v) { _update = v; }
	void SetDraw(bool v) { _draw = v; }
	void SetTest(bool v) { _test = v; }
	void SetCalcTM(const tpl::Mat4& tm);
	void SetLocalTM(const tpl::Mat4 tm);

	qgDpct* GetParent() { return _link.parent; }
	kint GetSubCount() { return _link.sub_count; }
	bool AddSub(qgDpct* node);
	bool RemoveSub(qgDpct* node, bool checkall = true);
	void ClearSub();
	qgDpct* GetSub(const char* name);
	qgDpct* FindSub(kint id);

	bool UpdateTM();
	void UpdateSub(float adv);
};


//////////////////////////////////////////////////////////////////////////
// camera

//
#define QGCMRF_LAYOUT			K_BIT(0)
#define QGCMRF_STATIC			K_BIT(1)

#define QGCMRI_FPS				K_BIT(0)
#define QGCMRI_MAYA				K_BIT(1)

//
class KAPI qgCamera : public qgDpct
{
	KCH_SUBCLASS(qgCamera, qgDpct);

protected:
	kint				_flags;
	kint				_insts;

	qgParamCamera		_camp;
	qgParamProj			_proj;
	qgParamView			_view;

protected:
	qgCamera();
	~qgCamera();

public:
	K_OVR bool Update(float adv);

	void SetProj(float fov, float znear, float zfar);
	void SetProj(float aspect, float fov, float znear, float zfar);

	void SetView(const tpl::Vec3& at, const tpl::Vec3& ahead);
	void SetViewPos(const tpl::Vec3& at);
	void SetViewAhead(const tpl::Vec3& ahead);

	const qgParamCamera& GetParamCamera() { return _camp; }
	const qgParamProj& GetParamProj() { return _proj; }
	const qgParamView& GetParamView() { return _view; }

	const tpl::Mat4& GetProjTM() { return _camp.proj; }
	const tpl::Mat4& GetViewTM() { return _camp.view; }
	const tpl::Mat4& GetViPrTM() { return _camp.vipr; }
	const tpl::Mat4& GetInvViewTM() { return _camp.invv; }

	float GetProjAspect() { return _proj.aspect; }
	float GetProjFov() { return _proj.fov; }
	float GetProjMinZ() { return _proj.z.min; }
	float GetProjMaxZ() { return _proj.z.max; }
	const tpl::Vec2& GetProjZ() { return _proj.z; }

	const tpl::Vec3& GetViewUp() { return _view.up; }
	const tpl::Vec3& GetViewPos() { return _view.at; }
	const tpl::Vec3& GetViewAhead() { return _view.ah; }

	void SetProcessLayout(bool iswork);
	float GetDistanceSq(const tpl::Vec3& v);
	float GetDistance(const tpl::Vec3& v);

	tpl::Vec3 Project(const tpl::Vec3& v, const tpl::Mat4& world);

public:
	static qgCamera* New();
};


//////////////////////////////////////////////////////////////////////////
// FPS camera

#define QGCMRI_CAN_FLY			K_BIT(16)
#define QGCMRI_CAN_TURN			K_BIT(17)

enum qgFpsWhere
{
	QSFPSWMV_FORWARD = 0,
	QSFPSWMV_BACKWARD = 1,
	QSFPSWMV_LEFT = 2,
	QSFPSWMV_RIGHT = 3,
	QSFPSWMV_UP = 4,
	QSFPSWMV_DOWN = 5,
	QSFPSWTN_LEFT = 6,
	QSFPSWTN_RIGHT = 7,
	QSFPSWTN_UP = 8,
	QSFPSWTN_DOWN = 9
};

#define QSFPSA_MOVE_FORWARD			HB_BIT(QSFPSWMV_FORWARD)
#define QSFPSA_MOVE_BACKWARD		HB_BIT(QSFPSWMV_BACKWARD)
#define QSFPSA_MOVE_LEFT			HB_BIT(QSFPSWMV_LEFT)
#define QSFPSA_MOVE_RIGHT			HB_BIT(QSFPSWMV_RIGHT)
#define QSFPSA_MOVE_UP				HB_BIT(QSFPSWMV_UP)
#define QSFPSA_MOVE_DOWN			HB_BIT(QSFPSWMV_DOWN)
#define QSFPSA_TURN_LEFT			HB_BIT(QSFPSWTN_LEFT)
#define QSFPSA_TURN_RIGHT			HB_BIT(QSFPSWTN_RIGHT)
#define QSFPSA_TURN_UP				HB_BIT(QSFPSWTN_UP)
#define QSFPSA_TURN_DOWN			HB_BIT(QSFPSWTN_DOWN)

class KAPI qgFpsCamera : public qgCamera
{
	KCH_SUBCLASS(qgFpsCamera, qgCamera);

protected:
	kint				_ables;

	float				_spdmov;
	float				_spdang;

	tpl::Mat4			_rot;
	tpl::Vec3			_vrot;
	tpl::Vec3			_mov;
	tpl::Vec3			_ang;

	tpl::Vec3			_lmov;
	tpl::Vec3			_lang;

	tpl::Vec2			_pt[2];
	kbyte				_keys[24];

protected:
	qgFpsCamera();
	~qgFpsCamera();

public:
	K_OVR bool Update(float adv);
	K_OVR void SetRotation(const tpl::Quat& q);

	void SetRotation(const tpl::Vec3& v);
	void RefreshAngle(const tpl::Vec3& ang, float delta);
	void RefreshAngle(float delta);
	void RefreshMove(const tpl::Vec3& move, float delta);
	void RefreshMove(float delta);

	float GetMoveSpeed() { return _spdmov; }
	float GetAngleSpeed() { return _spdang; }
	bool CanFly() { return K_OFLAG(_insts, QGCMRI_CAN_FLY); }

	void SetFly(bool v);
	void SetMoveSpeed(float v);
	void SetAngleSpeed(float v);
	void SetKey(qgFpsWhere w, kbyte k);

	void MovePoint(float x, float y, float adv);
	void MoveKey(qgFpsWhere w);

public:
	static qgFpsCamera* New();
};


//////////////////////////////////////////////////////////////////////////
// maya camera
class KAPI qgMayaCamera : public qgCamera
{
	KCH_SUBCLASS(qgMayaCamera, qgCamera);

public:
	float				_dist;

	float				_spdmov;
	float				_spdang;

	tpl::Mat4			_rot;
	tpl::Vec3			_vrot;
	tpl::Vec3			_mov;
	tpl::Vec3			_ang;

	tpl::Vec3			_lmov;
	tpl::Vec3			_lang;

	tpl::Vec2			_pt[2];
	kbyte				_keys[24];

protected:
	qgMayaCamera();
	~qgMayaCamera();

public:
	K_OVR bool Update(float adv);
	K_OVR void SetRotation(const tpl::Quat& q);

	void SetRotation(const tpl::Vec3& v);

	void RefreshAngle(const tpl::Vec3& angle, float delta);
	void RefreshAngle(float delta);

	void RefreshMove(const tpl::Vec3& move, float delta);
	void RefreshMove(float delta);

	float GetMoveSpeed() { return _spdmov; }
	float GetAngleSpeed() { return _spdang; }
	float GetFar() { return _dist; }
	const tpl::Vec3& GetRotationVec() { return _vrot; }

	void SetFar(float dist) { _dist = dist; }
	void SetMoveSpeed(float v);
	void SetAngleSpeed(float v);
	void SetKey(qgFpsWhere w, kbyte k);

	void MovePoint(float x, float y, float adv);
	void MoveKey(qgFpsWhere w);

public:
	static qgMayaCamera* New();
};

#endif	// __INC_QGRDPCT_H__